package com.jiaozi.core.aspect;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jiaozi.base.utils.IpUtil;
import com.jiaozi.base.utils.LogUtil;
import com.jiaozi.base.utils.WebUtil;
import com.jiaozi.base.utils.bean.R;
import com.jiaozi.core.annotation.Log;
import com.jiaozi.core.security.utils.SecurityUtil;
import com.jiaozi.system.entity.SysOperateLog;
import com.jiaozi.system.service.SysOperateLogService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class HttpRequestAspect {
    @Autowired
    private SysOperateLogService sysOperateLogService;
    /**
     * 定义切点
     */
    @Pointcut(value = "execution(* com.jiaozi.system.controller.*.*(..))")
    public void cutOffPoint() {
    }

//    @Pointcut(value = "execution(* com.jiaozi.biz.controller.*.*(..))")
//    public void cutOffPointLoginInfo() {
//    }


    @Pointcut(value = "execution(* com.jiaozi.core.security.controller.LoginController.login(..))")
    public void noCutOffPoint() {
    }


    @Around("(cutOffPoint())&&!noCutOffPoint()")
    public R doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        StopWatch sw=new StopWatch();
        sw.start();

        log.info("AOP---开始");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取被切入方法对象
        Method method = signature.getMethod();
        //获取方法上的Log注解
        Log alog = method.getAnnotation(Log.class);
        if (alog != null) {
            if (StrUtil.isNotBlank(alog.value())) {
                LogUtil.setLogPrefix(alog.value());
            }
        }
        //请求类名
        String className = joinPoint.getTarget().getClass().getName();
        //请求方法
        String methodName = joinPoint.getSignature().getName();
        String requestParam = "";
        // 获取请求参数
        if (HttpMethod.PUT.name().equals(WebUtil.getRequest().getMethod()) || HttpMethod.POST.name().equals(WebUtil.getRequest().getMethod())) {
            requestParam = argsArrayToString(joinPoint.getArgs());

        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) WebUtil.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            requestParam = paramsMap.toString();
        }
        APClassMethodInfo apClassMethodInfo = new APClassMethodInfo();
        apClassMethodInfo.setMethodName(methodName);
        apClassMethodInfo.setClassName(className);
        apClassMethodInfo.setAlog(alog);
        apClassMethodInfo.setRequestParam(requestParam);

        if (!WebUtil.getRequest().getMethod().equals(HttpMethod.GET.name())) {
//            记录日志信息
            logInfo(apClassMethodInfo);
        }
        log.info("AOP---请求地址：{}---请求参数：{}",WebUtil.getRequest().getRequestURI(),requestParam);
//        发起请求
        R result = (R) joinPoint.proceed();
        if(StrUtil.isNotBlank(LogUtil.getToken())){
            result.setToken(LogUtil.getToken());
        }
        sw.stop();
        log.info("AOP---结束---耗时:{}毫秒---返回结果:{}", sw.getTotalTimeMillis(), JSONObject.toJSONString(result));
//       清空请求链路信息
        clear();
        return result;
    }


    /**
     * 组装日志信息
     *
     * @return
     */
    public void logInfo(APClassMethodInfo apClassMethodInfo) {
        SysOperateLog apOperateLog = new SysOperateLog();
        if(apClassMethodInfo.getAlog()!=null&&StrUtil.isNotBlank(apClassMethodInfo.getAlog().value())){
            apOperateLog.setTitle(apClassMethodInfo.getAlog().value());
        }



        apOperateLog.setTranceId(LogUtil.getTraceId());
        apOperateLog.setOperName(SecurityUtil.getLoginUser().getUsername());
        apOperateLog.setRequestUri(WebUtil.getRequest().getRequestURI());
        apOperateLog.setRequestMethod(WebUtil.getRequest().getMethod());
        apOperateLog.setRequestParam(apClassMethodInfo.getRequestParam());
        apOperateLog.setRequestTime(new Date());
        String ip = IpUtil.getIpAddr(WebUtil.getRequest());
        apOperateLog.setRequestIp(ip);
        apOperateLog.setClassName(apClassMethodInfo.getClassName());
        apOperateLog.setMethodName(apClassMethodInfo.getMethodName());
        sysOperateLogService.save(apOperateLog);

    }




    /**
     * 移除 MDC
     */
    public void clear() {
        LogUtil.removeTraceId();
        LogUtil.removeLogPrefix();
    }


    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params += jsonObj.toString() + " ";
                    } catch (Exception e) {
                    }
                }
            }
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    private boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }



    @Data
    public class APClassMethodInfo {
        /**
         * 请求类名
         */
        private String className;
        /**
         * 请求方法
         */
        private String methodName;


        /**
         * 请求log 注解
         */
        private Log alog;
        /**
         * 请求参数
         */
        private String requestParam;



    }

}
